/*
 * filesystem.c
 *
 *  Created on: Oct 18, 2013
 *      Author: Ken Arok
 */

#include <string.h>
#include <stdlib.h>
#include "filesystem.h"


static FATFS filesystem_object[_VOLUMES];


fs_status_t vFileSystem_ScanFile(const char *_path)
{
	FRESULT _res;
	FILINFO _fno;
	DIR _dir;
	int i;
	char *_fn;	/* This function is assuming non-Unicode cfg. */
	char _extpath[_MAX_LFN + 1], _exit = 1;

	#if _USE_LFN
		static char _lfn[_MAX_LFN + 1];
		_fno.lfname = _lfn;
		_fno.lfsize = sizeof(_lfn);
	#endif

	_extpath[0] = '\0';
	strcat(_extpath, _path);

	do {
		/* Open the directory */
		_res = f_opendir(&_dir, _extpath);
		if (_res == FR_OK) {
			i = strlen(_extpath);

			/* Read a directory item */
			_res = f_readdir(&_dir, &_fno);
			/* Break on error or end of dir */
			if (_res != FR_OK || _fno.fname[0] == 0) _exit = 0;
			/* Ignore dot entry */
			if (_fno.fname[0] == '.') {}

			#if _USE_LFN
				_fn = *_fno.lfname ? _fno.lfname : _fno.fname;
			#else
				_fn = _fno.fname;
			#endif

			/* It is a directory */
			if (_fno.fattrib & AM_DIR) {
				_extpath[i] = '/';
				_extpath[i + 1] = '\0';
				strcat(_extpath, (const char *)_fn);
			}
			else {
				/* It is a file. */
				_exit = 0;
			}
		}
		else {
			_exit = 0;
		}
	}while(_exit);

	return _res;
}


fs_status_t vFileSystem_MountDrive(uint8_t _vol)
{
	if(_vol >= _VOLUMES) return FS_NOT_READY;

	return f_mount(_vol, &filesystem_object[_vol]);
}


fs_status_t vFileSystem_UmountDrive(uint8_t _vol)
{
	if(_vol >= _VOLUMES) return FS_NOT_READY;

	return f_mount(_vol, NULL);
}


fs_status_t vFileSystem_NewFile(const char *_path)
{
	FIL _fp;
	FRESULT _res;

	_res = f_open(&_fp, _path, FA_WRITE | FA_READ | FA_CREATE_NEW);
	if(_res == FR_OK) {
		_res = f_close(&_fp);
	}

	return _res;
}


fs_status_t vFileSystem_NewFolder(const char *_path)
{
	return f_mkdir(_path);
}

fs_status_t vFileSystem_CreateDirectory(const char *_path)
{
	uint8_t i, _sub = 1;
	uint16_t _len;
	FRESULT _res;
	char *_ptr, *_buff = NULL, *_curr_dir = NULL, *_copy_path = NULL;

	/* Calculate length of path */
	_len = strlen(_path);
	_len++;

	/* Allocate buffer for copy path. */
	_copy_path = malloc(_len);
	if(!_copy_path) return FS_MKFS_ABORTED;

	_ptr = _copy_path;

	/* Copy the path to temporary. */
	strcpy(_copy_path, _path);

	/* Allocate buffer for change directory operation. */
	_buff = malloc(255);
	if(!_buff) {
		free(_copy_path);
		return FS_MKFS_ABORTED;
	}

	/* Allocate buffer for current directory path. */
	_curr_dir = malloc(255);
	if(!_curr_dir) {
		free(_buff);
		free(_copy_path);
		return FS_MKFS_ABORTED;
	}

	f_getcwd(_curr_dir, 255);

	do {
		i = 1;
		while((*(_ptr + i) != '/') && (*(_ptr + i) != '\0')) {
			i++;
		}

		if(*(_ptr + i) == '/') {
			*(_ptr + i) = '\0';
			if(*_ptr == '/') {
				_res = f_mkdir((const char *)(_ptr + 1));
			}
			else {
				_res = f_mkdir((const char *)_ptr);
			}

			if(_res != FR_OK) {
				_sub = 0;
			}
			else {
				f_getcwd(_buff, 255);
				strcat(_buff, (const char *)_ptr);
				_res = f_chdir((const char *)_buff);
				if(_res != FR_OK) {
					_sub = 0;
				}
				else {
					*(_ptr + i) = '/';
					_ptr = _ptr + i;
				}
			}
		}
		else {
			if(*_ptr == '/') {
				_res = f_mkdir((const char *)(_ptr + 1));
			}
			else {
				_res = f_mkdir((const char *)_ptr);
			}

			_sub = 0;
		}
	}while(_sub);

	f_chdir((const char *)_curr_dir);

	free(_copy_path);
	free(_buff);
	free(_curr_dir);
	return _res;
}


fs_status_t vFileSystem_OpenFile(FIL *_fp, const char *_path)
{
	return f_open(_fp, _path, FA_WRITE | FA_READ | FA_OPEN_EXISTING);
}


fs_status_t vFileSystem_Browse(const char *_path)
{
	return f_chdir(_path);
}


fs_status_t vFileSystem_CloseFile(FIL *_fp)
{
	return f_close(_fp);
}


fs_status_t vFileSystem_EditFile(FIL *_fp, const void *_pbuf, uint32_t _ptr_offset, uint8_t _flag)
{
	unsigned int _btw, _bw;
	unsigned long _curr_ptr;
	FRESULT _res;

	switch(_flag)
	{
		case FILESYSTEM_APPEND:
			_curr_ptr = f_size(_fp);
		break;

		case FILESYSTEM_FOWARD:
			_curr_ptr = f_tell(_fp) + _ptr_offset;
		break;

		case FILESYSTEM_REWIND:
			if(f_tell(_fp) > _ptr_offset) {
				_curr_ptr = f_tell(_fp) - _ptr_offset;
			}
			else {
				_curr_ptr = 0;
			}
		break;

		case FILESYSTEM_OVERWRITE:
			_curr_ptr = 0;
		break;

		default:
			_curr_ptr = 0;
		break;
	}

	_btw = strlen((const portCHAR *)_pbuf);
	f_lseek(_fp, _curr_ptr);
	_res = f_write(_fp, _pbuf, _btw, &_bw);
	if(_res == FR_OK) {
		_res = f_sync(_fp);
	}

	return _res;
}
